package com.codename1.samples;


import static com.codename1.ui.CN.*;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.Label;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.io.Log;
import com.codename1.ui.Toolbar;
import java.io.IOException;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.io.NetworkEvent;
import com.codename1.io.Socket;
import com.codename1.io.Socket.StopListening;
import com.codename1.io.SocketConnection;
import com.codename1.ui.Button;
import com.codename1.ui.CN;
import com.codename1.ui.Container;
import com.codename1.ui.TextArea;
import com.codename1.ui.TextField;
import com.codename1.ui.layouts.BorderLayout;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * This file was generated by <a href="https://www.codenameone.com/">Codename One</a> for the purpose 
 * of building native mobile applications using Java.
 */
public class SocketSample {

    private Form current;
    private Resources theme;

    public void init(Object context) {
        // use two network threads instead of one
        updateNetworkThreadCount(2);

        theme = UIManager.initFirstTheme("/theme");

        // Enable Toolbar on all Forms by default
        Toolbar.setGlobalToolbar(true);

        // Pro only feature
        Log.bindCrashProtection(true);

        addNetworkErrorListener(err -> {
            // prevent the event from propagating
            err.consume();
            if(err.getError() != null) {
                Log.e(err.getError());
            }
            Log.sendLogAsync();
            Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
        });        
    }
    
    public void start() {
        if(current != null){
            current.show();
            return;
        }
        Form hi = new Form("Socket Sample", BoxLayout.y());
        Button server = new Button("Server Sockets");
        Button client = new Button("Client Socket");
        server.addActionListener(evt->{
            new ServerForm().show();
        });
        
        client.addActionListener(evt->{
            new ClientForm().show();
        });
        
        hi.add(new Label("Select mode")).add(server).add(client);
        hi.show();
    }

    public void stop() {
        current = getCurrentForm();
        if(current instanceof Dialog) {
            ((Dialog)current).dispose();
            current = getCurrentForm();
        }
    }
    
    public void destroy() {
    }
    
    public static class ServerConnection extends SocketConnection {
        ServerForm form;
        InputStream input;
        OutputStream output;
        
        public ServerConnection() {
            
        }
        
        ServerConnection(ServerForm form) {
            this.form = form;
        }
        
        void close() {
            try {
                if (input != null) {
                    input.close();
                }

            } catch (Throwable t) {}
            
            try {
                if (output != null) {
                    output.close();
                } 
            } catch (Throwable t) {
                
            }
            input = null;
            output = null;
            CN.callSerially(()->{
                form.connections.remove(this);
            });
        }

        void log(String text) {
            if (!CN.isEdt()) {
                CN.callSerially(()->{
                    log(text);
                });
                return;
            }
            System.out.println(text);
            form.response.setText(form.response.getText()+text+"\n");
            
        }
        
        void initForm() {
            if (!CN.isEdt()) {
                CN.callSerially(()->initForm());
                return;
            }
            if (form == null) {
                form = (ServerForm)CN.getCurrentForm();
            }
        }
        
        @Override
        public void connectionError(int errorCode, String message) {
            initForm();
            log("Connection error: "+message+" code="+errorCode);
        }

        @Override
        public void connectionEstablished(InputStream is, OutputStream os) {
            initForm();
            log("Connection established");
            input = is;
            output = os;
            
            
            try {
                byte[] buffer = new byte[8192];
                while(isConnected()) {
                    int pending = is.available();
                    if(pending > 0) {
                        int size = is.read(buffer, 0, 8192);
                        if(size == -1) {
                            return;
                        }
                        if(size > 0) {
                            String msg = new String(buffer, 0, size);
                            log("Received: " +msg);
                            output.write(("You said '"+msg).getBytes("UTF-8"));
                        }
                    } else {
                        Thread.sleep(50);
                    }
                }
                //onDisconnect();
            } catch(Exception err) {
                err.printStackTrace();
                //onDisconnect();
            }
                    
            
            
        }
    }
    
    public static final int PORT = 52378;
    
    class ServerForm extends Form {
        List<ServerConnection> connections = new ArrayList();
        StopListening serverHandle;
        Button start = new Button("Start Server");
        Button stop = new Button("Stop Server");
        TextArea response = new TextArea();
        ServerForm() {
            super("Server Form", new BorderLayout());
            Container north = BorderLayout.centerEastWest(null, BoxLayout.encloseX(start, stop), null);
            add(NORTH, north);
            add(CENTER, response);
            stop.setEnabled(false);
            start.addActionListener(evt->{
                if (serverHandle != null) {
                    return;
                }
                serverHandle = Socket.listen(PORT, ServerConnection.class);
                start.setEnabled(false);
                stop.setEnabled(true);
            });
            
            stop.addActionListener(evt-> {
                if (serverHandle == null) {
                    return;
                }
                serverHandle.stop();
                serverHandle = null;
                start.setEnabled(true);
                stop.setEnabled(false);
                
            });
            
        }
    }

    
    class ClientForm extends Form {
        TextArea response = new TextArea();
        InputStream input;
        OutputStream output;
        SocketConnection conn;
        TextField host = new TextField();
        Button connect = new Button("Connect");
        Button disconnect = new Button("Disconnect");
        Button send = new Button("Send");
        ClientForm() {
            super("Client Form", new BorderLayout());
            connect.setEnabled(true);
            disconnect.setEnabled(false);
            send.setEnabled(false);
            
            
            TextField message = new TextField();
            message.setHint("Message to server");
            
            TextField timeout = new TextField("0");
            timeout.setHint("Timeout");
            
            host.setHint("Hostname");
            
            Container north = new Container(BoxLayout.y());
            north.add(BorderLayout.centerEastWest(host, BoxLayout.encloseX(connect, disconnect, new Label("Timeout:"),timeout), null));
            north.add(BorderLayout.centerEastWest(message, send, null));
            add(NORTH, north);
            add(CENTER, response);
            
            connect.addActionListener(evt->{
                connect(Integer.parseInt(timeout.getText()));
            });
            
            send.addActionListener(evt->{
                send(message.getText());
            });
            
            disconnect.addActionListener(evt-> {
                disconnect();
            });
            
        }
        
        void log(String text) {
            System.out.println(text);
            response.setText(response.getText()+text+"\n");
            
        }
        
        void send(String message) {
            log("Sending message '"+message+"'");
            try {
                output.write(message.getBytes("UTF-8"));
            } catch (IOException ex) {
                log("Failed: "+ex.getMessage());
                Log.e(ex);
            }
        }
        
        void onConnect() {
            connect.setEnabled(false);
            disconnect.setEnabled(true);
            send.setEnabled(true);
            host.setEnabled(false);
        }
        
        void onDisconnect() {
            connect.setEnabled(true);
            disconnect.setEnabled(false);
            send.setEnabled(false);
            host.setEnabled(true);
            
        }
        
        void disconnect() {
            log("Disconnecting...");
            try {
                if (input != null) {
                    input.close();
                }
            } catch (Throwable t) {
                
            }
            try {
                if (output != null) {
                    output.close();
                }
            } catch (Throwable t) {
                
            }
            onDisconnect();
        }
        
        void connect(int timeout) {
            if (conn != null && conn.isConnected()) {
                log("Already connected");
                return;
            }
            Socket.connect(host.getText(), PORT, new SocketConnection(timeout) {
                @Override
                public void connectionError(int errorCode, String message) {
                    
                    log("Error: "+message+" Code: "+errorCode+"\n");
                    conn = null;
                    onDisconnect();
                }

                @Override
                public void connectionEstablished(InputStream is, OutputStream os) {
                    log("Connection established");
                    conn = this;
                    input = is;
                    output = os;
                    onConnect();
                    try {
                        byte[] buffer = new byte[8192];
                        while(isConnected()) {
                            int pending = is.available();
                            if(pending > 0) {
                                int size = is.read(buffer, 0, 8192);
                                if(size == -1) {
                                    return;
                                }
                                if(size > 0) {
                                    log("Received: " +new String(buffer, 0, size));
                                }
                            } else {
                                Thread.sleep(50);
                            }
                        }
                        onDisconnect();
                    } catch(Exception err) {
                        err.printStackTrace();
                        onDisconnect();
                    }
                    
                }
                
                
            });
            
            
        }
    }
}
